package org.go.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Iterator;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.go.domain.GoDomain;
import org.go.expcetion.ValidationException;
import org.go.log.Log;
import org.go.log.LoggerFactory;
import org.go.spi.ClassLoadHelper;
import org.go.spi.impl.CascadingClassLoadHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * 
 * @author hejie
 *
 */
public class XmlUtil {
	private static final Log log = LoggerFactory.getLog(XmlUtil.class);

	private static Document builderDocument(InputStream inputStream) throws Exception {
		try {
			// 使用DocumentBuilderFactory工厂类实例化一个DocumentBuilder类对象。
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			// 使用parse方法解析xml文件，生成一个Document对象
			Document document = builder.parse(inputStream);
			if (document == null) {
				throw new NullPointerException("构建Document失败,Document为null");
			}
			return document;
		} catch (Exception e) {
			throw new Exception("构建Document失败,Document为null");
		}
	}

	@SuppressWarnings("unused")
	private static Object resolveSchemaSource() {
		ClassLoadHelper classLoadHelper = new CascadingClassLoadHelper();
		InputSource inputSource = null;
		InputStream is = null;
		try {
			is = classLoadHelper.getResourceAsStream(GoDomain.GO_XSD_PATH_IN_JAR);
		} finally {
			if (is != null) {
				inputSource = new InputSource(is);
				inputSource.setSystemId(GoDomain.QUARTZ_SCHEMA_WEB_URL);
				log.debug("Utilizing schema packaged in local go distribution jar.");
			} else {
				log.info("Unable to load local schema packaged in go distribution jar. Utilizing schema online at {}", GoDomain.QUARTZ_SCHEMA_WEB_URL);
				return GoDomain.QUARTZ_SCHEMA_WEB_URL;
			}
		}
		return inputSource;
	}

	/**
	 * 
	 * @return
	 * @throws ParserConfigurationException
	 */
	protected static DocumentBuilder initDocumentParser() throws ParserConfigurationException {
		DocumentBuilder docBuilder = null;
		//XPath xpath = null;
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
		//docBuilderFactory.setNamespaceAware(true);
		//docBuilderFactory.setValidating(true);
		//docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
		//docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", resolveSchemaSource());
		docBuilder = docBuilderFactory.newDocumentBuilder();
		docBuilder.setErrorHandler(new ErrorHandler() {
			@Override
			public void error(SAXParseException exception) throws SAXException {

			}

			@Override
			public void fatalError(SAXParseException exception) throws SAXException {

			}

			@Override
			public void warning(SAXParseException exception) throws SAXException {

			}
		});
		return docBuilder;
	}

	public static XPath buildXpath() {
		NamespaceContext nsContext = new NamespaceContext() {
			@Override
			public String getNamespaceURI(String prefix) {
				if (prefix == null)
					throw new IllegalArgumentException("Null prefix");
				if (XMLConstants.XML_NS_PREFIX.equals(prefix))
					return XMLConstants.XML_NS_URI;
				if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
					return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
				if ("q".equals(prefix))
					return GoDomain.QUARTZ_NS;
				return XMLConstants.NULL_NS_URI;
			}

			@Override
			public String getPrefix(String namespaceURI) {
				// This method isn't necessary for XPath processing.
				throw new UnsupportedOperationException();
			}

			@Override
			public Iterator getPrefixes(String namespaceURI) {
				// This method isn't necessary for XPath processing.
				throw new UnsupportedOperationException();
			}
		};
		XPath xpath = null;
		xpath = XPathFactory.newInstance().newXPath();
		xpath.setNamespaceContext(nsContext);
		return xpath;
	}

	public static Boolean getBoolean(XPath xpath, String elementName, Node parentNode) throws Exception {
		String str = getTrimmedToNullString(xpath, elementName, parentNode);
		if (str == null || str.equals("")) {
			return null;
		} else {
			return new Boolean(str);
		}
	}

	/**
	 * 
	 * @param fileName
	 * @return
	 */
	public static InputStream getInputStream(String fileName) {
		ClassLoadHelper classLoadHelper = new CascadingClassLoadHelper();
		classLoadHelper.initialize();
		return classLoadHelper.getResourceAsStream(fileName);
	}

	public static int getTrimmedToInt(XPath xpath, String elementName, Node parentNode, int defaultVal) throws XPathExpressionException {
		String str = getTrimmedToNullString(xpath, elementName, parentNode);
		if (str == null || str.equals("")) {
			return defaultVal;
		}
		return Integer.valueOf(str).intValue();
	}

	public static Node getTrimmedToNullObject(XPath xpath, String elementName, Node parentNode) throws XPathExpressionException {
		Node str = (Node) xpath.evaluate(elementName, parentNode, XPathConstants.NODE);
		return str;
	}

	public static String getTrimmedToNullString(XPath xpath, String elementName, Node parentNode) throws XPathExpressionException {
		String str = (String) xpath.evaluate(elementName, parentNode, XPathConstants.STRING);

		if (str != null)
			str = str.trim();

		if (str != null && str.length() == 0)
			str = null;

		return str;
	}

	/**
	 * 使用递归循环xml中的每个节点
	 *
	 * @param nodeList 节点集合
	 */
	public static void loopNode(NodeList nodeList) {
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node node = nodeList.item(i);
			System.out.println(node.getNodeName());
			if ("note".equals(node.getNodeName())) {
				System.out.println("分类为：" + node.getAttributes().getNamedItem("category").getNodeValue());
				// 如果note节点有子节点，则开始递归
				if (node.hasChildNodes())
					loopNode(node.getChildNodes());
			} else if ("title".equals(node.getNodeName())) {
				System.out.println("标题：" + node.getTextContent());
			} else if ("message".equals(node.getNodeName())) {
				System.out.println("详细信息：" + node.getTextContent() + "\n");
			}
		}
	}

	public static Document processFile(InputStream inputStream, String systemId) throws SAXException, IOException, Exception {
		log.info("Parsing XML file: {} with systemId: {}", systemId, systemId);
		if (inputStream != null) {
			DocumentBuilder docBuilder = initDocumentParser();
			InputSource is = new InputSource(inputStream);
			is.setSystemId(systemId);
			// load the document
			//docBuilder
			Document document = docBuilder.parse(is);
			//Document document = docBuilder.parse(is);
			//maybeThrowValidationException();
			return document;
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param fileName
	 * @param systemId
	 * @throws ValidationException
	 * @throws SAXException
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws ParseException
	 * @throws XPathException
	 * @throws Exception
	 */
	public static Document processFile(String fileName, String systemId) throws ValidationException, SAXException, IOException, ClassNotFoundException, ParseException, XPathException, Exception {
		log.info("Parsing XML file: {} with systemId: {}", fileName, systemId);
		InputStream inputStream = getInputStream(fileName);
		if (inputStream != null) {
			DocumentBuilder docBuilder = initDocumentParser();
			InputSource is = new InputSource(inputStream);
			is.setSystemId(systemId);
			// load the document
			Document document = docBuilder.parse(is);
			//Document document = docBuilder.parse(is);
			//maybeThrowValidationException();
			return document;
		} else {
			return null;
		}
	}

	public static Document readXML(InputStream inputStream) throws Exception {
		// 先解析构建一个Document对象
		Document document = builderDocument(inputStream);
		// 获取根元素，因为只有一个
		//Element element = document.getDocumentElement();
		//System.out.println("根元素：" + element.getNodeName() + "\n");
		// 获取根元素下的子节点集合
		//NodeList nodeList = element.getChildNodes();
		// 使用递归遍历子节点集合中每个节点的信息
		//loopNode(nodeList);
		return document;
	}

	/*
	 * 构建XML文档的Document对象
	 * @xmlFileName xml文件路径
	 */
	@SuppressWarnings("unused")
	private void builderDocument(String xmlFileName) {
		if (xmlFileName == null || xmlFileName.length() == 0)
			throw new NullPointerException("构建Document对象时，所需的xml文件的文件名不能为null或者empty");
		try {
			// 使用DocumentBuilderFactory工厂类实例化一个DocumentBuilder类对象。
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			// 使用parse方法解析xml文件，生成一个Document对象
			Document document = builder.parse(new File(xmlFileName));
			if (document == null)
				throw new NullPointerException("构建Document失败,Document为null");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//	protected int getTrimmedToInt(XPath xpath, String elementName, Node parentNode, int defaultVal) throws XPathExpressionException {
	//		String str = getTrimmedToNullString(xpath, elementName, parentNode);
	//		if (str == null || str.equals("")) {
	//			return defaultVal;
	//		}
	//		return Integer.valueOf(str).intValue();
	//	}
	//
	//	protected Node getTrimmedToNullObject(XPath xpath, String elementName, Node parentNode) throws XPathExpressionException {
	//		Node str = (Node) xpath.evaluate(elementName, parentNode, XPathConstants.NODE);
	//		return str;
	//	}
	//
	//	protected String getTrimmedToNullString(XPath xpath, String elementName, Node parentNode) throws XPathExpressionException {
	//		String str = (String) xpath.evaluate(elementName, parentNode, XPathConstants.STRING);
	//
	//		if (str != null)
	//			str = str.trim();
	//
	//		if (str != null && str.length() == 0)
	//			str = null;
	//
	//		return str;
	//	}
}
